package com.kizuki.redis;

import org.junit.Test;
import redis.clients.jedis.BitOP;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.ListPosition;
import redis.clients.jedis.Transaction;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;


/**
 * api官网或者 https://www.runoob.com/redis/redis-sets.html
 */
public class TestRedis {

    @Test
    public void testRedis() {
        Jedis jedis = new Jedis("localhost");
        System.out.println("连接redis成功 " + jedis.ping());
        jedis.flushDB();
        System.out.println(jedis.dbSize());
    }

    @Test
    public void testString(){
        Jedis jedis = new Jedis("localhost");
        jedis.flushDB();

        jedis.set("k1", "v1");
        jedis.set("k2", "v2");
        jedis.set("k3", "v3");
        // 多个的set mget
        jedis.mset("k4", "v4", "k5", "v5");
        System.out.println(jedis.mget("k3", "k5"));

        jedis.append("k3", "+++");

        jedis.set("num0", "1");
        jedis.incr("num0");
        jedis.incrBy("num0", 2);
        jedis.decr("num0");
        jedis.decrBy("num0", 1);

        System.out.println(jedis.setnx("k3", "测试setnx"));
        // 多个设置
        System.out.println(jedis.msetnx("k3", "测试setnx"));

        /**
         * setex:[set] + [ex]pire
         */
        jedis.expire("num0", 4);
        jedis.setex("倒计时", 5, "倒计时value");
        System.out.println("num0 = " + jedis.ttl("num0"));
        System.out.println("倒计时 = " + jedis.ttl("倒计时"));

        Set<String> keys = jedis.keys("*");
        keys.forEach(k -> System.out.println("key = " + k + " value = " + jedis.get(k)));
    }

    @Test
    public void testList(){
        Jedis jedis = new Jedis("localhost");
        jedis.flushDB();

        /**
         * 其实是双端队列 可以左右操作
         * lpush 左插 rpush 右插 lpop rpop
         * llen 长度
         * lrem 移除特定的值
         * ltrim 截取list 会改变原来的list
         * lset 更新特定的值
         * linsert 在某个值之前或者之后插入一个值
         */
        jedis.lpush("list", "0");
        jedis.lpush("list", "1");
        jedis.lpush("list", "2");
        jedis.rpush("list", "3");
        jedis.rpush("list", "3");
        jedis.rpush("list", "3");
        jedis.rpush("list", "3");

        jedis.lpop("list");
        jedis.rpop("list");
        System.out.println(jedis.lindex("list", 1));
        System.out.println(jedis.llen("list"));
        jedis.lset("list", 0, "testlist");
        System.out.println("-------------------------");

        jedis.lrem("list", 1, "3"); // 可以删除n个

//        jedis.ltrim("list", 0, 2);
        jedis.linsert("list", ListPosition.AFTER, "0", "0之后");
        jedis.rpoplpush("list", "list1");
        System.out.println("list1长度 = " + jedis.llen("list1"));

        List<String> list = jedis.lrange("list", 0, -1);
        list.forEach(System.out::println);
    }

    @Test
    public void testSet(){
        Jedis jedis = new Jedis("localhost");
        jedis.flushDB();

        jedis.sadd("set", "0");
        jedis.sadd("set", "1");
        jedis.sadd("set", "2");
        // set 数量
        System.out.println(jedis.scard("set"));

        // 获取随机
        System.out.println(jedis.srandmember("set"));
        // 随机pop
        System.out.println(jedis.spop("set"));

        System.out.println(jedis.sismember("set", "4"));
        jedis.srem("set", "6");

        Set<String> set = jedis.smembers("set");
        set.forEach(System.out::println);

        jedis.sadd("set1", "0");

        // 交集
        System.out.println(jedis.sinter("set", "set1"));
        // 并集
        System.out.println(jedis.sunion("set", "set1"));
        // 差异
        System.out.println(jedis.sdiff("set", "set1"));
    }

    @Test
    public void testHash(){
        Jedis jedis = new Jedis("localhost");
        jedis.flushDB();

        HashMap<String, String> h0 = new HashMap<>();
        HashMap<String, String> h1 = new HashMap<>();
        h0.put("name", "k1");
        h0.put("id", "10");
        h0.put("id2", "100");
        h0.put("id3", "100");

        h1.put("h1key0", "h1value0");
        h1.put("h1key1", "h1value1");

        jedis.hmset("h0", h0);
        jedis.hset("h3", "name", "kizuki");

        Map<String, String> h3 = jedis.hgetAll("h0");
        h3.forEach((a,b) -> System.out.print(a + " " + b + " "));
        System.out.println();

        jedis.hdel("h0", "id2", "id3");
        System.out.println("id2 " + jedis.hexists("h0", "id2"));
        System.out.println("id " + jedis.hexists("h0", "id"));
        jedis.hincrBy("h0", "id", 2);
        System.out.println(jedis.hmget("h0", "id", "id2")); // 不存在则是null

        Set<String> h01 = jedis.hkeys("h0");
        List<String> h02 = jedis.hvals("h0");
        System.out.println("key:");
        h01.forEach(System.out::println);
        System.out.println("value:");
        h02.forEach(System.out::println);

        System.out.println(jedis.hsetnx("h0", "id", "test"));

        System.out.println("jedis.hlen = " + jedis.hlen("h0"));
    }

    @Test
    public void testSortedSet(){
        Jedis jedis = new Jedis("localhost");
        jedis.flushDB();

        HashMap<String, Double> map = new HashMap<>();
        map.put("kizuki", 1.0);
        map.put("madao", 2.0);
        jedis.zadd("top", map);
        jedis.zadd("top", 3.0, "xiaoming");

        jedis.zincrby("top", 11, "kizuki");
        System.out.println("zscore = " + jedis.zscore("top", "kizuki"));
        System.out.println("尝试删除 = " + jedis.zrem("top", "madao1", "ttt"));
        System.out.println("top的长度是" + jedis.zcard("top"));
        System.out.println("top的min和max之间的count是" + jedis.zcount("top", 0, 2));

        /**
         * 默认zrange(名次排名)
         * zrangeByScore(score权值)
         * zrangeByLex(字典区间)
         * revrange 反顺序 withScores 返回结果带有scores
         */
        System.out.println("zrange = " + jedis.zrange("top", 0, 1));
        System.out.println("zrevrange = " + jedis.zrevrange("top", 0, 5));
        System.out.println("zrangeWithScores = " + jedis.zrangeWithScores("top", 0, -1));
        System.out.println("zrevrangeByScoreWithScores = " + jedis.zrevrangeByScoreWithScores("top", 100, 0));
        System.out.println("zrangeByScoreWithScores = " + jedis.zrangeByScoreWithScores("top", 0, 5));
        /**
         * ZRANGEBYLEX key min max [LIMIT offset count]
         * min max 可用- + 代替,  [闭区间 (开区间
         * offset count
         */
        System.out.println("0 = " + jedis.zrangeByLex("top", "-" , "+"));
        System.out.println("1 = " + jedis.zrangeByLex("top", "-" , "+", 1, 3));
        System.out.println("2 = " + jedis.zrangeByLex("top", "-" , "[madao")); // ??? 感觉api有问题

        /**
         * 删除0至0的元素
         */
        jedis.zremrangeByRank("top", 0, 0);
        System.out.println(jedis.zrange("top", 0, 10));
    }

    @Test
    public void testBitmap(){
        Jedis jedis = new Jedis("localhost");
        jedis.flushDB();

        jedis.set("k1", "1"); // 01
        jedis.set("k2", "2"); // 10
        /**
         * 按位超过offset return true
         */
        System.out.println(jedis.getbit("k1", 0));
        System.out.println(jedis.getbit("k1", 100));
        /**
         * BitOP.OR 等是位运算
         */
        jedis.bitop(BitOP.OR, "k3", "k1", "k2");
        System.out.println(jedis.get("k3"));
    }


    /**
     * 单个 Redis 命令的执行是原子性的
     * Redis 事务类似于批处理  编译错误都不执行，运行错误则错误的命令不执行
     */
    @Test
    public void testShiWu(){
        Jedis jedis = new Jedis("localhost");
        jedis.flushDB();

        jedis.set("k1", "v1");
        jedis.set("k2", "v2");
        jedis.mset("k3", "v3", "k1", "v1");
        jedis.mset("k3", "v3", "k4", "v4");
        System.out.println(jedis.get("k3"));
        System.out.println(jedis.get("k4"));

        Transaction multi = jedis.multi(); // 开启事务
//        jedis.watch("k1"); // 乐观锁 watch !!!!
        try {
            // 编译错误都不执行，运行错误则错误的命令不执行
            multi.set("k1", "v1"); // 命令入队
            multi.set("k5", "v5");
            int i = 1/0;
            multi.exec(); // exec 执行事务
        } catch (Exception e) {
            multi.discard(); // discard 取消事务
            System.out.println("发生异常 事务取消");
        } finally {
            System.out.println("result = " + jedis.get("k5"));
            jedis.close();
        }

    }

}
